using System;
using System.Diagnostics;

namespace Cmpp.Client.Cmpp3Message.Utility {
    internal class BufferManager {
        private readonly object m_syncLock;
        private int m_availableSize;
        private byte[] m_buffer;

        public BufferManager() : this(1024 * 8) {
        }

        public BufferManager(int capacity) {
            m_syncLock = new object();

            m_availableSize = 0;
            m_buffer = new byte[capacity];
        }

        public void WriteBuffer(byte[] buffer, int offset, int count) {
            byte[] segmentBuffer = new byte[count];
            Buffer.BlockCopy(buffer, offset, segmentBuffer, 0, count);

            WriteBuffer(segmentBuffer);
        }

        public void WriteBuffer(byte[] buffer) {
            lock (m_syncLock) {
                EnsureCapacity(m_availableSize + buffer.Length);
                Buffer.BlockCopy(buffer, 0, m_buffer, m_availableSize, buffer.Length);
                m_availableSize += buffer.Length;
            }
        }

        private void EnsureCapacity(int expectedCapacity) {
            if (expectedCapacity <= m_buffer.Length) {
                return;
            }

            // increment the buffer capacity.
            byte[] newBuffer = new byte[expectedCapacity * 2];
            Buffer.BlockCopy(m_buffer, 0, newBuffer, 0, m_availableSize);
            m_buffer = newBuffer;

            Debug.WriteLine("resize the buffer capacity: " + newBuffer.Length, "BufferManager");
        }

        public byte[] ReadPacket() {
            byte[] buffer = new byte[0];
            lock (m_syncLock) {
                // read the packet length.
                int packetLength = (int) DataConvert.BytesToUInt32(m_buffer, 0);
                if (packetLength > m_availableSize) {
                    return buffer;
                }

                buffer = new byte[packetLength];
                Buffer.BlockCopy(m_buffer, 0, buffer, 0, packetLength);
                RemoveBuffer(0, packetLength);
                Array.Clear(m_buffer, packetLength, m_buffer.Length - m_availableSize - packetLength);
            }
            return buffer;
        }

        private void RemoveBuffer(int startIndex, int count) {
            Buffer.BlockCopy(m_buffer, startIndex, m_buffer, 0, count);
            m_availableSize -= count;
        }
    }
}